Helper.using('py.Figures', function (ns) {
    var __ = function (type, context) {
        context['is' + type] = true;
    };

    Object.prototype.setType = function (type) {
        return __(type, this);
    };

    ns.Drawable = function () {
        EventTarget.call(this);
        var self = this, _visual, _layer;

        this.setType('Drawable');

        this.addShadow = function () {
            _visual.setShadow({
                color:'black',
                blur:20,
                offset:[0, 0],
                alpha:0.9
            });
            _visual.getLayer().draw(); // chua chac la _layer
        };

        this.removeShadow = function () {
            _visual.setShadow({alpha:0});
            _visual.getLayer().draw();
        };

        this.visual = function (value) {
            if (typeof value === 'undefined') {
                return _visual;
            }

            if (_visual != value) {
                _visual = value;
            }
            return self;
        };

        this.layer = function (value) {
            if (typeof value === 'undefined') {
                return _layer;
            }

            if (_layer != value) {
                _layer = value;
            }
            return self;
        };

        this.draw = function (viewPort) {
            if (!_layer && !this.formed()) {
                return false;
            }
            this.update(viewPort);
            _layer.draw();
            if ("pixel" == _visual.getDetectionType()) {
                _visual.saveData();
            }
        };


        this.drawFunc = function () {
        };
        this.initVisual = function () {
            _layer.add(_visual);
        };

        this.formed = Helper.abstractMethod();
    };

    ns.Figure = function (params) {
        var dependant = [];
        ns.Drawable.apply(this);
        this.setType('Figure');

        this.on('dependantAdded', function (event) {
            var figure = event.dependant;
            dependant.push(figure);
        });

        this.on('dependantRemoved', function (event) {
            var figure = event.dependant;
            dependant.remove(figure);
        });
    };

    ns.ClosedFigure = function (params) {
        ns.Figure.call(this, params);
        ns.setType('ClosedFigure')
    };

    ns.ClosedFigure.prototype.getArea = Helper.abstractMethod();
    ns.ClosedFigure.prototype.getPerimeter = Helper.abstractMethod();
    ns.ClosedFigure.prototype.getBound = Helper.abstractMethod();

    ns.LinearObject = function (params) {
        ns.Figure.call(this, params);
        this.setType('LinearObject');

        this.getAnchor = Helper.abstractMethod();
        this.getDirectionVector = Helper.abstractMethod();
        this.getMagnitude = Helper.abstractMethod();

        this.getEquation = function () {
            var vector = this.getDirectionVector(), anchor = this.getAnchor(),
                a1 = vector.getX(), a2 = vector.getY(),
                x = anchor.getX(), y = anchor.getY(),
                a, b, c;

            a = a2;
            b = -a1;
            c = x * a2 - y * a1;
            return new py.Math.LineEquation(a, b, c);
        }

    };

    ns.DependentObject = function () {
        this.setType('DependentObject');

        var _depended = [];

        this.dependOn = function (figure) {
            if (!Helper.inArray(figure, _depended)) {
                _depended.push(figure);
                this.fire({ type:'dependantAdded', dependant:figure});
            }
        };

        this.removeDependency = function (figure) {
            if (Helper.inArray(figure, _depended)) {
                _depended.remove(figure);
                this.fire({type:'dependantRemoved', dependant:figure});
            }
        };
    };

    ns.Conic = function (params) {
        ns.Figure.call(this, params);
        this.setType('Conic');

        this.getFoci = Helper.abstractMethod();

//        this.getPointsOfTangency = function(p){
//            /*
//             * tiếp điểm có tọa độ là nghiệm của hệ sau
//             *  ax + by + c = 0
//             *  dx^2 + ey^2 + fxy + gx + hy + i = 0 (this.Equation)
//             */
//            var eq = this.getEquation(),
//                x = p.getX(),
//                y = p.getY();
//            var a = eq.a * x + eq.c / 2 * y + eq.d / 2;
//            var b = eq.b * y + eq.c / 2 * x + eq.e / 2;
//            var c = eq.d / 2 * x + eq.e / 2 * y + eq.f;
//
//            listRoot = null;
//            listPoint = new List<Point>();
//
//            listRoot = EquationHelper.SolveEquation(a, b, c, Equation.A, Equation.B, Equation.C, Equation.D, Equation.E, Equation.F);
//
//
//            //2011/10/17 HocOT add check if listRoot is null
//            if(listRoot != null)
//            {
//                foreach (var equationRoot in listRoot)
//                {
//                    Point temp = Point.CreatePoint(equationRoot.X, equationRoot.Y);
//                    listPoint.Add(temp);
//                }
//            }
//
//            return listPoint;
//        }

        this.getEquation = Helper.abstractMethod();

    };

    ns.Movable = function (params) {
        if (this.visual()) {
            this.setType('Movable');
            this.startMove = function () {
                this.visual().setDraggable(true);
            };

            this.stopMove = function () {
                this.visual().setDraggable(false);
            }
        } else {
            throw new Error('Only Drawable is Movable');
        }
    };


});

